/**
 * Verify | 2017 MIT License
 * 官方文档：http://lkx2217.cn
 * 依赖文件：Lkx、Popup
 * 模块化前端管理框架 - UI 模块
 * */

(function(win) {

    "use strict";

    //共同获取方法
    //返回基础的document对象
    //基础的querySelector获取方法
    //创建对象的createElement方法
    var native = {
            doc: function() { return document; },
            query: function(dom) { return native.doc().querySelector(dom); },
            queryAll: function(dom) { return native.doc().querySelectorAll(dom); },
            create: function(dom) { return native.doc().createElement(dom); }
        },

        //公共字段
        //记录空字段
        //记录现提供的验证规则
        //记录现提供的验证错误文案
        //返回错误文案
        field = {
            dom: null,
            reset: null,
            storageRules: [],
            empty: /^[ ]*$/,
            errorList: [],
            verifyName: ['number', 'phone', 'checkId', 'english', 'chinese', 'email', 'data'],
            verifyText: {
                numberText: '必须为数字',
                phoneText: '手机号码不正确',
                checkIdText: '身份证号码不正确',
                englishText: '必须为英语字母',
                chineseText: '必须为中文汉字',
                emailText: 'email 格式不正确',
                dataText: '日期格式不正确'
            },
            verifyRules: {
                numberRules: /^\d+$/,
                phoneRules: /^0?(13[0-9]|15[012356789]|17[013678]|18[0-9]|14[57])[0-9]{8}$/,
                checkIdRules: /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i,
                englishRules: /^[a-zA-Z]+$/,
                chineseRules: /^[\u4e00-\u9fa5]+$/,
                emailRules: /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/,
                dataRules: /^(?:(?!0000)[0-9]{4}-?.(?:(?:0[1-9]|1[0-2])-?.(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/
            },

            sameText: '内容必须相同',
            emptyText: '内容不能为空',
            maxText: '不能多于',
            minText: '必须大于',
            lengthText: '必须为',
            successText: '验证成功'
        },

        //共同使用方法
        //包括判断数据类型是否正常
        //删增class操作
        common = {

            //判断所需字段的数据类型是否符合规范
            str: function(text) { return (typeof text === 'string' && !field.empty.test(text)) ? true : false; },
            num: function(parameter) { return typeof parameter === 'number' ? true : false; },
            boo: function(parameter) { return typeof parameter === 'boolean' ? true : false; },
            fun: function(parameter) { return typeof parameter === 'function' ? true : false; },
            obj: function(parameter) {
                var hasObj;
                for (var o in parameter) {
                    hasObj = true;
                    break;
                }
                return (typeof parameter === 'object' && hasObj) ? true : false;
            },

            //查找dom对象的class值
            hasClass: function(dom, cls) {
                cls = cls || '';
                if (cls.replace(/\s/g, '').length === 0) {
                    return false;
                }
                return new RegExp(' ' + cls + ' ').test(' ' + dom.className + ' ');
            },

            //增加doc对象的class值
            addClass: function(dom, cls) {
                if (!common.hasClass(dom, cls)) {
                    dom.className = dom.className === '' ? cls : dom.className + ' ' + cls;
                }
            },

            //删除doc对象的class值
            removeClass: function(dom, cls) {
                if (common.hasClass(dom, cls)) {
                    var newClass = ' ' + dom.className.replace(/[\t\r\n]/g, '') + ' ';
                    while (newClass.indexOf(' ' + cls + ' ') >= 0) {
                        newClass = newClass.replace(' ' + cls + ' ', ' ');
                    }
                    dom.className = newClass.replace(/^\s+|\s+$/g, '');
                }
            },

            //判断验证对象
            //此方法主要判断是通过变量传输还是.或#的形式传送对象
            //从而进行转换成框架统一识别对象
            domTurn: function(dom, direction) {
                if (common.str(dom)) {
                    direction === 'bind' ? field.dom = native.queryAll(dom) : field.reset = native.queryAll(dom);
                } else if (common.num(dom.length)) {
                    direction === 'bind' ? field.dom = dom : field.reset = dom;
                } else {
                    var oper = {
                        0: dom,
                        length: '1'
                    };

                    direction === 'bind' ? field.dom = oper : field.reset = oper;
                }
            },

            //显示验证
            //通过reset方法所来，忽略对象验证并对其进行显示操作
            show: function(resetDom) {

                var domId = resetDom.getAttribute('id');

                resetDom.setAttribute('verifyHidden', 'false');
                resetDom.setAttribute('verifyStatus', '0');
                resetDom.setAttribute('verifyRules', field.storageRules[resetDom.getAttribute('verifyHiddenNum')]);

                common.addClass(resetDom, 'Lkx-Verify-show');
                common.removeClass(resetDom, 'Lkx-Verify-hide');

                //若是嵌入式风格进入
                //显示风格所生成的错误文案
                if (domId !== null) {
                    var label = native.query('label[for=' + domId + ']');
                    common.addClass(label, 'Lkx-Verify-show');
                    common.removeClass(label, 'Lkx-Verify-hide');
                }
            },

            //隐藏验证
            //通过reset方法所来，忽略对象验证并对其进行隐藏操作
            hide: function(resetDom, index, direction) {

                var domId = resetDom.getAttribute('id');

                field.storageRules[direction + index] = resetDom.getAttribute('verifyRules');

                resetDom.setAttribute('verifyHidden', 'true');
                resetDom.setAttribute('verifyStatus', '1');
                resetDom.setAttribute('verifyRules', '');
                resetDom.setAttribute('verifyHiddenNum', direction + index);

                common.addClass(resetDom, 'Lkx-Verify-hide');
                common.removeClass(resetDom, 'Lkx-Verify-show');

                //若是嵌入式风格进入
                //隐藏风格所生成的错误文案
                if (domId !== null) {
                    var label = native.query('label[for=' + domId + ']');
                    common.addClass(label, 'Lkx-Verify-hide');
                    common.addClass(label, 'Lkx-Verify-show');
                }
            }
        };

    //验证主要逻辑
    //巡查匹配各个验证规则以及使用的规则
    //匹配成功进行验证处理
    //未匹配成功进行成功处理
    var Logic = function(dom, index, verify) {

        this.dom = dom;
        this.index = index;
        this.verifyRules = dom.getAttribute('verifyRules');
        this.value = dom.value;

        //获取构造函数Verify的this值
        //针对性的进行赋值
        this.verify = verify;
        this.style = verify.style;
        this.noText = verify.noText;
        this.noFrame = verify.noFrame;
        this.parentFrame = verify.parentFrame;

        this.styleChoice();
    };

    Logic.prototype = {

        //风格选择
        //默认为0号风格
        //判断风格是否符合规范，若是超出规范恢复成默认风格
        styleChoice: function() {

            var that = this;

            //验证风格初始化判断
            if (common.num(that.style)) {
                range();
            } else if (common.str(that.style)) {
                that.style = parseInt(that.style);
                range();
            } else {
                that.style = 0;
            }

            //验证范围判断
            function range() {
                if (that.style > 1 || that.style < 0) {
                    that.style = 0;
                }
            }

            that.empty();
        },

        //必填项验证
        //内容为必填，空或空格等都被视为error
        empty: function() {
            if (this.verifyRules && this.verifyRules.indexOf('empty') >= 0) {
                if (common.str(this.value)) {
                    this.canFill();
                } else {
                    this.error(field.emptyText, this.dom);
                }
            } else if (this.verifyRules) {
                this.canFill();
            } else {
                this.dom.setAttribute('verifyStatus', '1');
            }
        },

        //可填项验证
        //内容为空是通过，有内容时，进行后续操作
        canFill: function() {
            if (this.verifyRules.indexOf('canFill') >= 0) {
                common.str(this.value) ? this.verifyQuery() : this.success(this.dom);
            } else {
                this.verifyQuery();
            }
        },

        //查询已经记录的验证规则
        //查看内部规则库，是否使用了规则
        verifyQuery: function() {
            for (var n = 0; n < field.verifyName.length; n++) {
                if (this.verifyRules.indexOf(field.verifyName[n]) >= 0) {
                    this.rules = field.verifyRules[field.verifyName[n] + 'Rules'];
                    this.verifyText = field.verifyText[field.verifyName[n] + 'Text'];
                    this.regular();
                    break;
                } else {

                    //不存在库里的规则，进行非库正则验证
                    if (n === field.verifyName.length - 1) {
                        this.appointLength();
                    }
                }
            }
        },

        //进行规则正则验证
        //验证对象输入内容是否匹配指定正则
        regular: function() {
            if (common.str(this.value) && this.rules.test(this.value)) {
                this.appointLength();
            } else {
                this.error(this.verifyText, this.dom);
            }
        },

        //指定长度
        //当存在指定长度时，不进去最大最小长判断
        appointLength: function() {

            if (this.verifyRules.indexOf('length') >= 0) {

                var lengthAfter = this.verifyRules.split('length:')[1],
                    lengthReal = lengthAfter.split(',')[0];

                if (this.value.length != lengthReal) {
                    this.error(field.lengthText + ' ' + lengthReal + ' 位数', this.dom);
                } else {
                    this.same();
                }
            } else {
                this.maxLength();
            }
        },

        //最大长度判断
        //验证对象的长度是否符合规范
        maxLength: function() {

            if (this.verifyRules.indexOf('max') >= 0) {

                var maxAfter = this.verifyRules.split('max:')[1],
                    maxReal = maxAfter.split(',')[0];

                if (this.value.length > maxReal) {
                    this.error(field.maxText + ' ' + maxReal + ' 位数', this.dom);
                } else {
                    this.minLength();
                }
            } else {
                this.minLength();
            }
        },

        //最小长度判断
        //验证对象的长度是否符合规范
        minLength: function() {

            if (this.verifyRules.indexOf('min') >= 0) {

                var minAfter = this.verifyRules.split('min:')[1];
                var minReal = minAfter.split(',')[0];

                if (this.value.length < minReal) {
                    this.error(field.minText + ' ' + minReal + ' 位数', this.dom);
                } else {
                    this.same();
                }
            } else {
                this.same();
            }
        },

        //相同验证
        //判断对象中是所有带same规则的内容是否相等
        same: function() {

            if (this.verifyRules.indexOf('same') >= 0) {

                var sameState = true,
                    sameIndex = 0,
                    sameThis = [],
                    sameValue = [];

                //找寻所有same的对象
                for (var d = 0; d < field.dom.length; d++) {
                    if (field.dom[d].getAttribute('verifyRules').indexOf('same') >= 0) {
                        sameThis[sameIndex] = field.dom[d];
                        sameValue[sameIndex] = field.dom[d].value;
                        sameIndex++;
                    }
                }

                //对所有带有same的对象进行验证
                for (var s = 0; s < sameThis.length; s++) {
                    for (var n = 0; n < sameThis.length; n++) {
                        if (sameValue[s] !== sameValue[n]) {
                            sameState = false;
                            break;
                        }
                    }
                }

                //根据验证成功弹出tips层
                for (var t = 0; t < sameThis.length; t++) {
                    if (sameState) {
                        this.success(sameThis[t]);
                    } else {
                        this.error(field.sameText, sameThis[t]);
                    }
                }

            } else {
                this.success(this.dom);
            }
        },

        //验证成功
        //当对象验证成功后，会默认抛出成功文案
        //抛出成功文案只用于0号风格
        success: function(dom) {

            dom.setAttribute('verifyStatus', '1');

            //是否不显示错误边框
            if (!this.noFrame) {

                //是否在父级显示边框
                //在几层父级显示错误聚焦边框
                if (common.num(this.parentFrame)) {
                    var domParent = dom;
                    for (var i = 0; i < this.parentFrame; i++) {
                        domParent = domParent.parentNode;
                    }
                    common.removeClass(domParent, 'Lkx-Verify-error');
                } else {
                    common.removeClass(dom, 'Lkx-Verify-error');
                }
            }

            //仅0号风格抛出成功文案
            //嵌入式风格将移除错误文案提示
            switch (this.style) {
                case 0:

                    Popup.tips(field.successText, dom, {
                        style: 'success'
                    });

                    break;
                case 1:

                    this.domId = dom.getAttribute('id');
                    this.label = native.query('label[for=' + this.domId + ']');

                    if (this.label) {
                        dom.style.paddingRight = '0px';
                        dom.parentNode.removeChild(this.label);
                    }

                    break;
            }

            return true;
        },

        //验证错误
        //将会根据风格形式弹出错误文案以及定位错误对象
        error: function(text, dom) {

            var that = this;

            dom.setAttribute('verifyStatus', '0');

            //是否不显示错误边框
            if (!this.noFrame) {

                //是否在父级显示边框
                //在几层父级显示错误聚焦边框
                if (common.num(this.parentFrame)) {
                    var domParent = dom;
                    for (var i = 0; i < this.parentFrame; i++) {
                        domParent = domParent.parentNode;
                    }
                    common.addClass(domParent, 'Lkx-Verify-error');
                } else {
                    common.addClass(dom, 'Lkx-Verify-error');
                }
            }

            //若是不需要文案提示
            //将会忽略任何文案提示
            if (!this.noText) {

                //错误文案抛出
                //0号风格，依赖Lkx.Popup下tips层展开
                //1号风格，内嵌式风格
                switch (that.style) {

                    case 0:

                        Popup.tips(text, dom, {
                            style: 'danger',
                            time: 5000
                        });

                        break;
                    case 1:

                        that.domId = dom.getAttribute('id');
                        that.label = native.query('label[for=' + that.domId + ']');

                        //对象id为空时，进行赋值id
                        if (that.domId === null) {
                            dom.setAttribute('id', 'Lkx-Verify-id-' + that.index);
                            that.domId = dom.getAttribute('id');
                        }

                        //查询页面中是否已经创建了此对象
                        labelQuery();

                        //实时改变label定位
                        dom.style.paddingRight = (that.label.offsetWidth + 15) + 'px';
                        that.label.style.left = (dom.offsetWidth + dom.offsetLeft) - (that.label.offsetWidth + 10) + 'px';

                        break;
                }
            }

            //标签查询方法
            //查询页面中是否出现同样的label标签
            function labelQuery() {

                if (!that.label) {

                    //创建label标签
                    that.label = native.create('label');

                    //将创建的label放入dom对象后端
                    dom.parentNode.appendChild(that.label);

                    //动态样式赋值
                    common.addClass(that.label, 'Lkx-Verify-embed');

                    that.label.setAttribute('for', that.domId);
                    that.label.style.lineHeight = dom.offsetHeight + 'px';
                    that.label.style.top = dom.offsetTop + 'px';

                    //初始化文案赋值
                    //此处label指向当前创建的label
                    that.label.innerHTML = text;

                } else {

                    //二次文案赋值
                    //此处label指向页面中存在的label
                    that.label.innerHTML = text;
                }
            }

            //记录错误文案
            //用于使用success暴露出去
            var error = {
                dom: dom,
                text: text
            };
            field.errorList.push(error);
        },
    };

    //向外暴露的Verify构造函数
    //通过此方法进行使用表单验证
    var Verify = function() {
        this.name = 'Lkx-Verify';
    };

    Verify.fn = Verify.prototype;

    //初始化绑定
    //一切验证皆以绑定的对象进行展开
    //支持.或#的标签选择符
    //支持任何变量形式传入对象
    Verify.fn.bind = function(dom, style) {

        var that = this;

        //进行传入对象判断及赋值
        common.domTurn(dom, 'bind');

        //对所选风格执行存储
        //用于Verify方法共存
        that.style = style;

        //循环所有绑定的对象
        //对其进行blur事件绑定
        for (var d = 0; d < field.dom.length; d++) {

            field.dom[d].index = d;
            field.dom[d].dom = field.dom[d];

            if (field.dom[d].getAttribute('verifyHidden') === 'true') {
                common.hide(field.dom[d].dom, field.dom[d].index, 'bind');
            }

            //初始化赋予失败状态
            field.dom[d].setAttribute('verifyStatus', '0');
            field.dom[d].addEventListener('blur', function() {
                new Logic(this.dom, this.index, that);
            });
        }
    };

    //成功验证判断
    //此方法用于表单提交时的依据
    //当返回true时，表示所有绑定的对象同以通过验证
    Verify.fn.success = function(fun) {

        var successVerify = true,
            reFun = common.fun(fun) ? fun : function() {};

        //清空记录的错误文案
        //错误文案将在每次执行success时，返回出去
        field.errorList = [];

        for (var d = 0; d < field.dom.length; d++) {

            field.dom[d].index = d;
            field.dom[d].dom = field.dom[d];

            new Logic(field.dom[d].dom, field.dom[d].index, this);

            if (field.dom[d].getAttribute('verifyStatus') === '0') { successVerify = false; }
        }

        //组装返回数据
        //分别是成功状态以及错误列表信息
        reFun({
            msg: successVerify,
            errorList: field.errorList
        });
    };

    //默认文案配置
    //此方法提供更改默认错误提示文案
    //更改错误标红位置
    //是否显示错误提示
    //是否显示标红外框
    Verify.fn.config = function(resource) {

        if (common.obj(resource)) {

            //是否显示错误提示文案
            this.noText = common.boo(resource.noText) ? resource.noText : false;
            //是否显示错误聚焦边框
            this.noFrame = common.boo(resource.noFrame) ? resource.noFrame : false;
            //是否将聚焦边框显示在父级
            this.parentFrame = resource.parentFrame;

            //修改默认错误提示文案
            if (common.obj(resource.text)) {
                if (common.str(resource.text.number)) { field.verifyText.numberText = resource.text.number; }
                if (common.str(resource.text.phone)) { field.verifyText.phoneText = resource.text.phone; }
                if (common.str(resource.text.checkId)) { field.verifyText.checkIdText = resource.text.checkId; }
                if (common.str(resource.text.english)) { field.verifyText.englishText = resource.text.english; }
                if (common.str(resource.text.chinese)) { field.verifyText.chineseText = resource.text.chinese; }
                if (common.str(resource.text.email)) { field.verifyText.emailText = resource.text.email; }
                if (common.str(resource.text.data)) { field.verifyText.dataText = resource.text.data; }
                if (common.str(resource.text.same)) { field.sameText = resource.text.same; }
                if (common.str(resource.text.empty)) { field.emptyText = resource.text.empty; }
                if (common.str(resource.text.max)) { field.maxText = resource.text.max; }
                if (common.str(resource.text.min)) { field.minText = resource.text.min; }
                if (common.str(resource.text.length)) { field.lengthText = resource.text.length; }
                if (common.str(resource.text.success)) { field.successText = resource.text.success; }
            }
        }
    };

    //隐藏方法
    //若是页面中出现不需要验证的表单且需要绑定验证待显示的时候才开始验证
    //可以尝试此方法进行隐藏以及忽略验证
    Verify.fn.reset = function(dom) {

        common.domTurn(dom, 'reset');

        for (var r = 0; r < field.reset.length; r++) {
            this.verifyHidden = field.reset[r].getAttribute('verifyHidden');

            if (common.str(this.verifyHidden)) {
                this.verifyHidden === 'true' ? common.show(field.reset[r]) : common.hide(field.reset[r], r, 'reset');
            } else {
                common.hide(field.reset[r], r, 'reset');
            }
        }
    };

    //增加自定义规则
    //若默认规则中没有中意的规则，可以使用此方法进行添加
    Verify.fn.addRules = function(resource) {
        if (common.obj(resource)) {
            if (common.obj(resource.rules)) {
                for (var r = 0; r < resource.rules.length; r++) {
                    if (common.str(resource.rules[r].name) && common.str(resource.rules[r].error) && typeof resource.rules[r].regular === 'object') {
                        field.verifyName.push(resource.rules[r].name);
                        field.verifyText[resource.rules[r].name + 'Text'] = resource.rules[r].error;
                        field.verifyRules[resource.rules[r].name + 'Rules'] = resource.rules[r].regular;
                    }
                }
            }
        }
    };

    win.Verify = new Verify();

})(window);